package com.junmeng.lib.cascade.util

import com.junmeng.lib.cascade.SourceCascadePlugin
import com.junmeng.lib.cascade.bean.Config
import org.gradle.api.initialization.Settings
import org.w3c.dom.Document
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import org.xml.sax.InputSource
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult

object IdeaGitVcsFileUtil {

    /**
     * vcs.xml文件如下
     * <?xml version="1.0" encoding="UTF-8" standalone="no"?>
     * <project version="4">
     * <component name="VcsDirectoryMappings">
     * <mapping directory="$PROJECT_DIR$" vcs="Git" />
     * </component>
     * </project>
     */
    const val DOT_GIT = ".git"
    const val VCS_NODE_PROJECT = "project"
    const val VCS_NODE_COMPONENT = "component"
    const val VCS_NODE_COMPONENT_ATTR_NAME = "name"
    const val VCS_NODE_COMPONENT_ATTR_NAME_VALUE = "VcsDirectoryMappings"

    const val VCS_NODE_MAPPING = "mapping"
    const val VCS_NODE_MAPPING_ATTR_DIRECTORY = "directory"
    const val VCS_NODE_MAPPING_ATTR_FLAG = "_flag"
    const val VCS_NODE_MAPPING_ATTR_FLAG_VALUE = "FromSourceCascadePlugin"
    const val VCS_NODE_MAPPING_ATTR_VCS = "vcs"
    const val VCS_NODE_MAPPING_ATTR_VCS_VALUE = "Git"

    fun handleGit(settings: Settings, config: Config) {
        val vcsXmlPath = "${settings.rootDir}${File.separator}.idea${File.separator}vcs.xml"
        val vcsFile = File(vcsXmlPath)
        if (!vcsFile.exists()) {
            Log.log("$vcsXmlPath 文件不存在")
            return
        }

        val factory = DocumentBuilderFactory.newInstance()
        val builder = factory.newDocumentBuilder()

        var doc: Document? = null
        try {
            doc = builder.parse(InputSource(vcsFile.reader()))
        } catch (e: Exception) {
            Log.log("vcs.xml文件解析失败:${e.message}")
        }
        if (doc == null) {
            return
        }

        //cascadeModuleMap存在且启用git管理时增加mapping节点，否则移除mapping节点
        if (config.enableCascade && config.enableGit && !config.cascadeModuleMap.isNullOrEmpty()) {
            addMappingNodes(doc, config.cascadeModuleMap)
        } else {
            //删除之前新增的节点
            removeMappingNodes(doc)
        }

        // 保存更改后的 XML 文件
        save(doc, vcsFile)

    }

    /**
     * 根据cascadeModuleMap新增<mapping>节点
     * @param doc Document
     * @param cascadeModuleMap Map<String, String>
     */
    private fun addMappingNodes(doc: Document, cascadeModuleMap: Map<String, String>) {
        val mappingNodeList: MutableList<Node> = getMappingNodeList(doc)
        val component: Node? =
            doc.getElementsByTagName(VCS_NODE_COMPONENT)?.item(0)
        cascadeModuleMap.forEach {
            if (it.key.isNullOrEmpty() || it.value.isNullOrEmpty()) {
                Log.log("${SourceCascadePlugin.CONFIG_CASCADE_MODULE_MAP}的key或者value不能为空")
                return
            }
            val file = File(it.value)
            val dir = findDirWhichExistDotGit(file)
            Log.log("工程(${it.key})的.git文件所在目录：${dir}")
            if (!dir.isNullOrEmpty()) {
                var find = mappingNodeList.find {
                    it.nodeName == VCS_NODE_MAPPING
                            && it.attributes?.getNamedItem(
                        VCS_NODE_MAPPING_ATTR_VCS
                    )?.nodeValue == VCS_NODE_MAPPING_ATTR_VCS_VALUE
                            && it.attributes?.getNamedItem(
                        VCS_NODE_MAPPING_ATTR_DIRECTORY
                    )?.nodeValue == dir
                }
                if (find != null) {
                    Log.log("发现已经存在目标节点，无需新增")
                } else {
                    val newMapping = doc.createElement(VCS_NODE_MAPPING)
                    newMapping.setAttribute(
                        VCS_NODE_MAPPING_ATTR_DIRECTORY,
                        dir
                    )
                    newMapping.setAttribute(
                        VCS_NODE_MAPPING_ATTR_VCS,
                        VCS_NODE_MAPPING_ATTR_VCS_VALUE
                    )
                    //新增此属性便于删除判断
                    newMapping.setAttribute(
                        VCS_NODE_MAPPING_ATTR_FLAG,
                        VCS_NODE_MAPPING_ATTR_FLAG_VALUE
                    )

                    //如果component节点不存在则新增
                    if (component == null) {
                        val component = doc.createElement(VCS_NODE_COMPONENT)
                        component.setAttribute(
                            VCS_NODE_COMPONENT_ATTR_NAME,
                            VCS_NODE_COMPONENT_ATTR_NAME_VALUE
                        )
                        doc.getElementsByTagName(VCS_NODE_PROJECT)?.item(0)?.appendChild(component)
                        component.appendChild(newMapping)
                    } else {
                        component?.appendChild(newMapping)
                    }
                    Log.log("已新增${VCS_NODE_MAPPING}节点：${VCS_NODE_MAPPING_ATTR_DIRECTORY}=${dir}")
                }
            }
        }
    }

    /**
     * 移除新增的<mapping>节点
     * @param doc Document
     */
    private fun removeMappingNodes(doc: Document) {
        val component: Node? =
            doc.getElementsByTagName(VCS_NODE_COMPONENT)?.item(0)
        val mappingNodeList: NodeList? = component?.childNodes
        mappingNodeList?.let {
            for (i in 0 until it.length) {
                val mapping = it.item(i)
                if (mapping != null && mapping.nodeName == VCS_NODE_MAPPING
                    && mapping.attributes?.getNamedItem(
                        VCS_NODE_MAPPING_ATTR_FLAG
                    )?.nodeValue == VCS_NODE_MAPPING_ATTR_FLAG_VALUE
                ) {
                    component?.removeChild(mapping)
                }
            }
        }
    }

    /**
     * 保存
     * @param doc Document
     * @param savePath File 要保存的文件路径
     */
    fun save(doc: Document, savePath: File) {
        val transformerFactory = TransformerFactory.newInstance()
        val transformer = transformerFactory.newTransformer()
        val source = DOMSource(doc)
        val result = StreamResult(savePath)
        transformer.transform(source, result)
    }


    /**
     * 找出存在".git"文件的目录
     * @param path 开始查找的路径，会逐级往上查找，找到则返回
     * @return 目录，null表示未找到
     */
    fun findDirWhichExistDotGit(path: File): String? {
        if (path == null || !path.exists()) {
            return null
        }
        if (path.isDirectory() && File(path, DOT_GIT).exists()) {
            return path.absolutePath
        }

        if (path.parent == null) {
            return null
        }
        val parent = File(path.parent)
        val tmp = File(parent, DOT_GIT)
        if (tmp.exists()) {
            return parent.absolutePath
        }
        return findDirWhichExistDotGit(parent)
    }

    /**
     * 获得<mapping>节点列表
     * @param doc Document
     * @return MutableList<Node>
     */
    private fun getMappingNodeList(doc: Document): MutableList<Node> {
        val mappingNodeList: MutableList<Node> = mutableListOf()
        val component: Node? =
            doc.getElementsByTagName(VCS_NODE_COMPONENT)?.item(0)
        component?.childNodes?.let {
            if (it.length > 0) {
                for (i in 0 until it.length) {
                    it.item(i)?.let {
                        mappingNodeList.add(it)
                    }
                }
            }
        }
        return mappingNodeList
    }
}